package com.db.priceApp.network;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.FileHandler;
import java.util.logging.Logger;

import com.db.priceApp.domain.clientDataBasa.ClientProfile;
import com.db.priceApp.network.protocol.Message;
import com.db.priceApp.network.protocol.MessageProcessor;
import com.db.priceApp.network.protocol.MessageTranciever;

public class Client implements Runnable {
	private volatile SocketChannel socketChannel;
	private volatile Selector selector;
	private volatile BufferedInputStream is;
	private volatile ClientProfile clientProfile;
	private Logger logger = Logger.getLogger(Client.class.getName());

	public Client() {
		is = new BufferedInputStream(System.in);
		clientProfile = new ClientProfile();
	}

	public SocketChannel getSocketChannel() {
		return socketChannel;
	}

	public SelectionKey getKey() {
		return clientProfile.getSelectionKey();
	}

	public ClientProfile getClientProfile (){
		return clientProfile;
	}
	
	public void run() {
		try {
			Set<SelectionKey> selectedKeys;
			while (true) {
				// wait an answer
				selector.select();
				selectedKeys = selector.selectedKeys();
				
				for (SelectionKey skey: selectedKeys) {
					if (skey.isReadable()) {
						readMessageFromChannel(skey);						
					}
				}
				selectedKeys.clear();
			}

		} catch (IOException e) {
			logger.severe("client failure: " + e.getMessage());
			throw new RuntimeException("client error", e);
		} finally {
			try {
				is.close();
				socketChannel.close();
				selector.close();
			} catch (IOException e) {
				logger.severe("client failed to shut down properly");
			}
		}

	}

	private void readMessageFromChannel(SelectionKey skey) throws IOException {
		byte[] serverResponse;
		Message serverMessage = new Message();
		MessageProcessor mp = new MessageProcessor();
		MessageTranciever mt = new MessageTranciever();

		mt.receive((SocketChannel) skey.channel());

		while (mt.hasMessage()) {
			try {
				serverResponse = mt.nextMessage();
				serverMessage = mp.processMessage(serverResponse,
						clientProfile.getLogin(), skey);
				logger.info(serverMessage.toString());
				
			} catch (IllegalArgumentException e) {
				logger.warning("Illegal message format: " + e.getMessage());
				continue;
			} 
		}
	}

	public void connectToSocket() {
		try {
			selector = Selector.open();
			socketChannel = SocketChannel.open();
			socketChannel.configureBlocking(true);
			socketChannel.connect(new InetSocketAddress("localhost", 1027));
			socketChannel.configureBlocking(false);

			SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ);
			clientProfile.setSelectionKey(key);

		} catch (IOException e) {
			logger.severe("couldn't open a channel");
			throw new RuntimeException("couldn't open a channel");
		}
	}

	public static void main(final String args[]) {
		Client client = new Client();
		client.connectToSocket();
		ClientMarketDataRequestsWriter worker = new ClientMarketDataRequestsWriter(
				client.getClientProfile());
		
		
		ExecutorService executorForClientRequestWriter = Executors
				.newSingleThreadExecutor();
		
		executorForClientRequestWriter.execute(worker);
		
		client.run();
		executorForClientRequestWriter.shutdown();
	}
}
